/*
 * Copyright (c) [2022] Huawei Technologies Co.,Ltd.All rights reserved.
 *
 * OpenArkCompiler is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR
 * FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#include "x64_cg.h"
#include "x64_isa.h"
#include "x64_MPISel.h"

namespace maplebe {
using namespace maple;

void X64MoveRegArgs::Run() {
  MoveRegisterArgs();
}

void X64MoveRegArgs::CollectRegisterArgs(std::map<uint32, X64reg> &argsList,
                                         std::vector<uint32> &indexList,
                                         std::map<uint32, X64reg> &pairReg,
                                         std::vector<uint32> &numFpRegs,
                                         std::vector<uint32> &fpSize) const {
  X64CGFunc *x64CGFunc = static_cast<X64CGFunc*>(cgFunc);
  uint32 numFormal = static_cast<uint32>(x64CGFunc->GetFunction().GetFormalCount());
  numFpRegs.resize(numFormal);
  fpSize.resize(numFormal);
  X64CallConvImpl parmlocator(x64CGFunc->GetBecommon());
  CCLocInfo ploc;
  uint32 start = 0;
  if (numFormal) {
    MIRFunction *func = const_cast<MIRFunction *>(x64CGFunc->GetBecommon().GetMIRModule().CurFunction());
    if (x64CGFunc->GetBecommon().HasFuncReturnType(*func)) {
      TyIdx tyIdx = x64CGFunc->GetBecommon().GetFuncReturnType(*func);
      if (x64CGFunc->GetBecommon().GetTypeSize(tyIdx) <= k16ByteSize) {
        start = 1;
      }
    }
  }
  for (uint32 i = start; i < numFormal; ++i) {
    MIRType *ty = x64CGFunc->GetFunction().GetNthParamType(i);
    parmlocator.LocateNextParm(*ty, ploc, i == 0, &x64CGFunc->GetFunction());
    if (ploc.reg0 == kRinvalid) {
      continue;
    }
    X64reg reg0 = static_cast<X64reg>(ploc.reg0);
    x64CGFunc->PushElemIntoFormalRegList(reg0);
    MIRSymbol *sym = x64CGFunc->GetFunction().GetFormal(i);
    if (sym->IsPreg()) {
      continue;
    }
    argsList[i] = reg0;
    indexList.emplace_back(i);
    if (ploc.reg1 == kRinvalid) {
      continue;
    }
    if (ploc.numFpPureRegs) {
      uint32 index = i;
      numFpRegs[index] = ploc.numFpPureRegs;
      fpSize[index] = ploc.fpSize;
      continue;
    }
    x64CGFunc->PushElemIntoFormalRegList(static_cast<X64reg>(ploc.reg1));
    pairReg[i] = static_cast<X64reg>(ploc.reg1);
    if (ploc.reg2 == kRinvalid) {
      continue;
    }
    x64CGFunc->PushElemIntoFormalRegList(static_cast<X64reg>(ploc.reg2));
    if (ploc.reg3 == kRinvalid) {
      continue;
    }
    x64CGFunc->PushElemIntoFormalRegList(static_cast<X64reg>(ploc.reg3));
  }
}

ArgInfo X64MoveRegArgs::GetArgInfo(std::map<uint32, X64reg> &argsList, std::vector<uint32> &numFpRegs,
                                   std::vector<uint32> &fpSize, uint32 argIndex) const {
  X64CGFunc *x64CGFunc = static_cast<X64CGFunc*>(cgFunc);
  ArgInfo argInfo;
  argInfo.reg = argsList[argIndex];
  argInfo.mirTy = x64CGFunc->GetFunction().GetNthParamType(argIndex);
  argInfo.symSize = x64CGFunc->GetBecommon().GetTypeSize(argInfo.mirTy->GetTypeIndex());
  argInfo.memPairSecondRegSize = 0;
  argInfo.doMemPairOpt = false;
  argInfo.createTwoStores  = false;
  argInfo.isTwoRegParm = false;

  argInfo.regType = (argInfo.reg < V0) ? kRegTyInt : kRegTyFloat;
  argInfo.sym = x64CGFunc->GetFunction().GetFormal(argIndex);
  CHECK_NULL_FATAL(argInfo.sym);
  argInfo.symLoc = static_cast<const X64SymbolAlloc*>(x64CGFunc->GetMemlayout()->
      GetSymAllocInfo(argInfo.sym->GetStIndex()));
  CHECK_NULL_FATAL(argInfo.symLoc);
  return argInfo;
}

bool X64MoveRegArgs::IsInSameSegment(const ArgInfo &firstArgInfo, const ArgInfo &secondArgInfo) const {
  if (firstArgInfo.symLoc->GetMemSegment() != secondArgInfo.symLoc->GetMemSegment()) {
    return false;
  }
  if (firstArgInfo.symSize != secondArgInfo.symSize) {
    return false;
  }
  if (firstArgInfo.symSize != k4ByteSize && firstArgInfo.symSize != k8ByteSize) {
    return false;
  }
  if (firstArgInfo.regType != secondArgInfo.regType) {
    return false;
  }
  return firstArgInfo.symLoc->GetOffset() + firstArgInfo.stkSize == secondArgInfo.symLoc->GetOffset();
}

void X64MoveRegArgs::GenerateStrInsn(ArgInfo &argInfo, X64reg reg2, uint32 numFpRegs, uint32 fpSize) {
  X64CGFunc *x64CGFunc = static_cast<X64CGFunc*>(cgFunc);
  int32 stOffset = x64CGFunc->GetBaseOffset(*argInfo.symLoc);
  CGRegOperand *baseOpnd = static_cast<CGRegOperand*>(x64CGFunc->GetBaseReg(*argInfo.symLoc));
  uint32 opndSize = GetPrimTypeSize(argInfo.sym->GetType()->GetPrimType()) * kBitsPerByte;
  CGRegOperand &regOpnd = x64CGFunc->GetOpndBuilder()->CreatePReg(argInfo.reg,
      opndSize, argInfo.regType);
  CGMemOperand *memOpnd = nullptr;
  memOpnd = &x64CGFunc->GetOpndBuilder()->CreateMem(opndSize);
  memOpnd->SetBaseRegister(*baseOpnd);
  memOpnd->SetBaseOfst(x64CGFunc->GetOpndBuilder()->CreateImm(baseOpnd->GetSize(), stOffset));

  MOperator mOp;
  if (opndSize == 8 * kBitsPerByte) {
    mOp = x64::MOP_movq_r_m;
  } else {
    mOp = x64::MOP_movl_r_m;
  }
  Insn &insn = x64CGFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
  insn.AddOperandChain(regOpnd).AddOperandChain(*memOpnd);
  x64CGFunc->GetCurBB()->AppendInsn(insn);
}

void X64MoveRegArgs::MoveRegisterArgs() {
  X64CGFunc *x64CGFunc = static_cast<X64CGFunc*>(cgFunc);
  BB *formerCurBB = x64CGFunc->GetCurBB();
  x64CGFunc->GetDummyBB()->ClearInsns();
  x64CGFunc->SetCurBB(*x64CGFunc->GetDummyBB());

  std::map<uint32, X64reg> movePara;
  std::vector<uint32> moveParaIndex;
  std::map<uint32, X64reg> pairReg;
  std::vector<uint32> numFpRegs;
  std::vector<uint32> fpSize;
  CollectRegisterArgs(movePara, moveParaIndex, pairReg, numFpRegs, fpSize);

  for (auto indexItem = moveParaIndex.begin(); indexItem != moveParaIndex.end(); ++indexItem) {
    uint32 index = *indexItem;
    ArgInfo argInfo = GetArgInfo(movePara, numFpRegs, fpSize, index);
    GenerateStrInsn(argInfo, pairReg[index], numFpRegs[index], fpSize[index]);
  }

  x64CGFunc->GetFirstBB()->InsertAtBeginning(*x64CGFunc->GetDummyBB());
  x64CGFunc->SetCurBB(*formerCurBB);
}
}  /* namespace maplebe */
